﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Data;
using BNF.Utility.TimeAPI;
using NYurik.TimeSeriesDb;
using DataEngine.DataModel;
using System.IO;
using BNF.HMI.DAL;
using DataEngine.ContractModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace DataEngine
{
    /// <summary>
    /// ArchiveDatabase
    /// Developer: HaiMai
    /// </summary>
    class ArchiveDatabase
    {

        /// Key: TagID, Value: DataType
        private Hashtable DataTypeMap = new Hashtable();
        private DataTable HistorianDB = new DataTable();
        private Hashtable WriterDB = new Hashtable();
        private Hashtable RealTimeDB = new Hashtable(); 

        private Object objLock = new Object();
        
        private CTAPI myT = new CTAPI();
        private string RootFolder = @"C:\LogicDataEngine\";
        private string currentFolder = "";
        private CTAPI myTime = new CTAPI();
        private UInt64 ArchiveDuration =2*3600*1000;

        #region SUPPORT FUNCTION
        private string GetFileName(UInt32 TagID)
        {
            return RootFolder + currentFolder + "\\" + TagID.ToString() + ".rdb";
        }
        private string GetFileName(UInt32 TagID,String p_currentFolder)
        {
            return RootFolder + p_currentFolder + "\\" + TagID.ToString() + ".rdb";
        }
        private void DefineTable()
        {
            HistorianDB.Columns.Add("TagID", Type.GetType("System.UInt32"));
            HistorianDB.Columns.Add("StartTime", Type.GetType("System.UInt64"));
            HistorianDB.Columns.Add("EndTime", Type.GetType("System.UInt64"));
            HistorianDB.Columns.Add("Handler", Type.GetType("System.Object"));
        }
        private void SimulateData()
        {
            for (int i = 0; i < 10; i++)
			{
                DataRow myRow = HistorianDB.NewRow();
                myRow["TagID"] = i+10;
                myRow["StartTime"] = myT.GetCurrentTimeAsJavaTime() - 10000;
                myRow["EndTime"] = myT.GetCurrentTimeAsJavaTime();
                myRow["Handler"] = new Object();
                HistorianDB.Rows.Add(myRow);
			}
        }

        /// Return only the handler which appropriate for TimeStamp 
        private List<Object> GetFileHandler(uint TagID, ulong TimeStamp, uint duration =0)
        {
            List<Object> myHandlerList = new List<Object>();

            bool isCurrent = (TimeStamp == 0);
            Object handler = WriterDB[TagID];

            if (!isCurrent)
            {
                UInt16 DataType = GetDataType(TagID);
                if (DataType == (UInt16)VarEnum.VT_BOOL)
                {
                    var bf = (IWritableFeed<ulong, ItemULngByte>)WriterDB[TagID];
                    if (bf != null && !bf.IsEmpty && bf.FirstIndex <= TimeStamp)
                    {
                        isCurrent = true;
                    }
                }
                else
                {
                    var bf = (IWritableFeed<ulong, ItemULngULng>)WriterDB[TagID];
                    if (bf != null && !bf.IsEmpty && bf.FirstIndex <= TimeStamp)
                    {
                        isCurrent = true;
                    }
                }
            }

            if (isCurrent)
            {
                myHandlerList.Add(handler);
            }

            // If the List Type
            if (duration > 0)
            {
                ulong myStartTime = TimeStamp - duration;
                var query = (from row in HistorianDB.AsEnumerable()
                             where row.Field<UInt32>("TagID") == TagID
                             && row.Field<UInt64>("EndTime") >= myStartTime
                             && row.Field<UInt64>("StartTime") <= TimeStamp
                             orderby row.Field<UInt64>("StartTime") descending
                             select row.Field<Object>("Handler")).ToList<Object>();

                if (query != null && query.Count > 0)
                {
                    myHandlerList.AddRange(query);
                }
            }
            else if(!isCurrent)
            {
                var query = (from row in HistorianDB.AsEnumerable()
                             where row.Field<UInt32>("TagID") == TagID
                             && row.Field<UInt64>("StartTime") <= TimeStamp
                             orderby row.Field<UInt64>("StartTime") descending
                             select row.Field<Object>("Handler")).ToList<Object>();

                if (query != null && query.Count > 0)
                {
                    myHandlerList.AddRange(query);
                }
            }

            myHandlerList.Reverse();
            return myHandlerList;
        }
        private UInt16 GetDataType(uint TagID)
        {
            if (!DataTypeMap.ContainsKey(TagID))
            {

                return 0;
            }
            else
            {
                return Convert.ToUInt16(DataTypeMap[TagID]);
            }

        }
        #endregion

        #region DB MOUNT/UNMOUNT
        private void InitFile(Object bf1, UInt16 DataType)
        {
            if (DataType == (UInt16)VarEnum.VT_BOOL)
            {
                var bf = (BinSeriesFile<ulong, ItemULngByte>)bf1;
                // 
                // Initialize new file parameters and create it
                //
                bf.UniqueIndexes = true; // enforce index uniqueness
                //bf.Tag = "Sample Data"; // optionally provide a tag to store in the file header
                bf.InitializeNewFile(); // Finish new file initialization and create an empty file
            }
            else
            {
                var bf = (BinSeriesFile<ulong, ItemULngULng>)bf1;
                // 
                // Initialize new file parameters and create it
                //
                bf.UniqueIndexes = true; // enforce index uniqueness
                //bf.Tag = "Sample Data"; // optionally provide a tag to store in the file header
                bf.InitializeNewFile(); // Finish new file initialization and create an empty file
            }
        }
        /// Mount all HistorianDB in 1 hours
        private void MountALLHistorianDB()
        {
            int Count = 0;
            DirectoryInfo myDBFolder = new DirectoryInfo(RootFolder);
            string previousFolder = DateTime.Now.AddHours(-1).ToString("yyyyMMddHH");
            foreach (DirectoryInfo myFolder in myDBFolder.GetDirectories())
            {
                Count = 0;
                if (myFolder.Name == previousFolder)
                {
                    // ============== Load ALL HistorianDB
                    foreach (DictionaryEntry item in DataTypeMap)
                    {
                        uint TagID = (uint)item.Key;
                        string fileName = GetFileName(TagID, myFolder.Name);
                        UInt16 DataType = (UInt16)DataTypeMap[TagID];
                        if (DataType == (UInt16)VarEnum.VT_BOOL) //tagVariant BOOL Type
                        {
                            var bf = (IWritableFeed<ulong, ItemULngByte>)BinaryFile.Open(fileName);
                            if (!bf.IsEmpty)
                            {
                                DataRow myRow = HistorianDB.NewRow();
                                myRow["TagID"] = TagID;
                                myRow["StartTime"] = bf.FirstIndex;
                                myRow["EndTime"] = bf.LastIndex;
                                myRow["Handler"] = bf;

                                HistorianDB.Rows.Add(myRow);
                                Count++;
                                if (!bf.IsEmpty)  RealTimeDB[TagID] = ProcessByteForSend(TagID, bf.Stream().Last<ItemULngByte>());
                            }
                        }
                        else
                        {
                            var bf = (IWritableFeed<ulong, ItemULngULng>)BinaryFile.Open(fileName);
                            if (!bf.IsEmpty)
                            {
                                DataRow myRow = HistorianDB.NewRow();
                                myRow["TagID"] = TagID;
                                myRow["StartTime"] = bf.FirstIndex;
                                myRow["EndTime"] = bf.LastIndex;
                                myRow["Handler"] = bf;

                                HistorianDB.Rows.Add(myRow);
                                Count++;
                                if (!bf.IsEmpty)  RealTimeDB[TagID] = ProcessULngForSend(TagID, bf.Stream().Last<ItemULngULng>());
                            }
                        }
                    }
                }

                if (Count > 0)
                {
                    Console.WriteLine(String.Format("===> MOUNTED For [{0}] Data Count[{1}]", myFolder.Name, Count));
                }
            }
            Console.WriteLine("===> MOUNTED ALL HistorianDB Count:" + HistorianDB.Rows.Count.ToString());
        }
        /// From Header to Historian 
        private void MountRealTime2HistorianDB()
        {
            // ============== Add to HistorianDB then Clear all items
            foreach (DictionaryEntry item in WriterDB)
            {
                uint TagID = (uint)item.Key;
                UInt32 DataType = Convert.ToUInt32(DataTypeMap[TagID]);
                if (DataType == (UInt16)VarEnum.VT_BOOL) //tagVariant BOOL Type
                {
                    var bfWriter = (IWritableFeed<ulong, ItemULngByte>)WriterDB[TagID];

                    DataRow myRow = HistorianDB.NewRow();
                    myRow["TagID"] = TagID;
                    myRow["StartTime"] = bfWriter.FirstIndex;
                    myRow["EndTime"] = bfWriter.LastIndex;
                    myRow["Handler"] = bfWriter;

                    HistorianDB.Rows.Add(myRow);
                }
                else
                {
                    var bfWriter = (IWritableFeed<ulong, ItemULngULng>)WriterDB[TagID];

                    DataRow myRow = HistorianDB.NewRow();
                    myRow["TagID"] = TagID;
                    myRow["StartTime"] = bfWriter.FirstIndex;
                    myRow["EndTime"] = bfWriter.LastIndex;
                    myRow["Handler"] = bfWriter;

                    HistorianDB.Rows.Add(myRow);
                }
            }

            Console.WriteLine("===> MOUNTED RealTime to HistorianDB Count:" + WriterDB.Count.ToString());
            WriterDB.Clear();

        }
        /// Mount Header DB in specific hour time (Now)
        private void MountWriterDB()
        {
            // ============== Create/Load all new items
            foreach (DictionaryEntry item in DataTypeMap)
            {
                uint TagID = (uint)item.Key;
                string fileName = GetFileName(TagID);
                UInt16 DataType = (UInt16)DataTypeMap[TagID];
                if (DataType == (UInt16)VarEnum.VT_BOOL) //tagVariant BOOL Type
                {
                    if (!File.Exists(fileName))
                    {
                        var bfWriter = new BinSeriesFile<ulong, ItemULngByte>(fileName); //Writer
                        InitFile(bfWriter, DataType);
                        WriterDB[TagID] = bfWriter;
                    }
                    else
                    {
                        var bfWriter = (IWritableFeed<ulong, ItemULngByte>)BinaryFile.Open(fileName, true);
                        WriterDB[TagID] = bfWriter;
                        if (!bfWriter.IsEmpty)
                            RealTimeDB[TagID] = ProcessByteForSend(TagID, bfWriter.Stream().Last<ItemULngByte>());
                    }
                }
                else
                {
                    if (!File.Exists(fileName))
                    {

                        var bfWriter = new BinSeriesFile<ulong, ItemULngULng>(fileName);
                        InitFile(bfWriter, DataType);
                        WriterDB[TagID] = bfWriter;
                    }
                    else
                    {
                        var bfWriter = (IWritableFeed<ulong, ItemULngULng>)BinaryFile.Open(fileName, true);
                        WriterDB[TagID] = bfWriter;
                        if (!bfWriter.IsEmpty)
                            RealTimeDB[TagID] = ProcessULngForSend(TagID, bfWriter.Stream().Last<ItemULngULng>());
                    }
                }
            }

            Console.WriteLine("===> MOUNTED WriterDB Count:" + WriterDB.Count.ToString());
        }
        /// Mount all new handler in specific hour time (Now -24)
        private void UnMountHistorianDB()
        {
            UInt64 removeTime = (UInt64)myT.GetCurrentTimeAsJavaTime() - ArchiveDuration;
            var query = (from row in HistorianDB.AsEnumerable()
                         where row.Field<UInt64>("EndTime") < removeTime
                         select row).ToList();


            // Remove from Database
            foreach (DataRow myRow in query)
            {

                uint TagID = (uint)myRow["TagID"];
                UInt32 DataType = Convert.ToUInt32(DataTypeMap[TagID]);
                if (DataType == (UInt16)VarEnum.VT_BOOL) //tagVariant BOOL Type
                {
                    var bf = (IWritableFeed<ulong, ItemULngByte>)myRow["Handler"];
                    bf.Dispose();
                }
                else
                {
                    var bf = (IWritableFeed<ulong, ItemULngULng>)myRow["Handler"];
                    bf.Dispose();
                }
                HistorianDB.Rows.Remove(myRow);
            }

            Console.WriteLine("===> UNMOUNT HistorianDB Count:" + HistorianDB.Rows.Count.ToString());
        }
        private bool IsDBChanged()
        {
            bool isChanged = false;
            if (currentFolder != DateTime.Now.ToString("yyyyMMddHH"))
            {
                currentFolder = DateTime.Now.ToString("yyyyMMddHH");
                Directory.CreateDirectory(RootFolder + currentFolder);
                isChanged = true;
            }

            return isChanged;
        }
        #endregion

        #region DATA PROCESSING
        private List<ItemULngULng> ProcessULngForStore(Queue<DATA_POINT> TagQueue)
        {
            List<ItemULngULng> myDataList = new List<ItemULngULng>();
            int listCount = TagQueue.Count;
            for (int i = 0; i < listCount; i++)
            {
                DATA_POINT theDataHolder = TagQueue.Dequeue();
                ItemULngULng myItem = new ItemULngULng(theDataHolder.TimeStamp, BitConverter.ToUInt64(theDataHolder.Value, 0));
                myDataList.Add(myItem);
            }

            return myDataList;
        }
        private List<ItemULngByte> ProcessByteForStore(Queue<DATA_POINT> TagQueue)
        {
            List<ItemULngByte> myDataList = new List<ItemULngByte>();
            int listCount = TagQueue.Count;
            for (int i = 0; i < listCount; i++)
            {
                DATA_POINT theDataHolder = TagQueue.Dequeue();
                ItemULngByte myItem = new ItemULngByte(theDataHolder.TimeStamp, theDataHolder.Value[0]);
                myDataList.Add(myItem);
            }

            return myDataList;
        }
        private List<Object> ProcessULngForSend(UInt32 TagID, UInt16 DataType, List<ItemULngULng> TagDataList)
        {
            List<Object> myDataList = new List<Object>();
            int listCount = TagDataList.Count;
            for (int i = 0; i < listCount; i++)
            {
                DATA_POINT theDataHolder = new DATA_POINT();
                theDataHolder.DataType = DataType;
                theDataHolder.TimeStamp = TagDataList[i].SequenceNum;
                theDataHolder.TagID = TagID;
                theDataHolder.Value = BitConverter.GetBytes(TagDataList[i].Value);
                myDataList.Add(theDataHolder);
            }

            return myDataList;
        }
        private List<Object> ProcessByteForSend(UInt32 TagID, List<ItemULngByte> TagDataList)
        {
            List<Object> myDataList = new List<Object>();
            int listCount = TagDataList.Count;
            for (int i = 0; i < listCount; i++)
            {
                DATA_POINT theDataHolder = new DATA_POINT();
                theDataHolder.DataType = (UInt16)VarEnum.VT_BOOL;
                theDataHolder.TimeStamp = TagDataList[i].SequenceNum;
                theDataHolder.TagID = TagID;
                theDataHolder.Value = new Byte[8];
                theDataHolder.Value[0] = TagDataList[i].Value;
                myDataList.Add(theDataHolder);
            }

            return myDataList;
        }
        private Object ProcessULngForSend(UInt32 TagID, ItemULngULng TagData)
        {
            DATA_POINT theDataHolder = new DATA_POINT();
            UInt16 DataType = GetDataType(TagID);
            theDataHolder.DataType = DataType;
            theDataHolder.TimeStamp = TagData.SequenceNum;
            theDataHolder.TagID = TagID;
            theDataHolder.Value = BitConverter.GetBytes(TagData.Value);

            return theDataHolder;
        }
        private Object ProcessByteForSend(UInt32 TagID, ItemULngByte TagData)
        {
            DATA_POINT theDataHolder = new DATA_POINT();
            theDataHolder.DataType = (UInt16)VarEnum.VT_BOOL;
            theDataHolder.TimeStamp = TagData.SequenceNum;
            theDataHolder.TagID = TagID;
            theDataHolder.Value = new Byte[8];
            theDataHolder.Value[0] = TagData.Value;

            return theDataHolder;
        }
        #endregion

        #region PUBLIC METHOD

        public void Init()
        {
            DefineTable();
            MountALLHistorianDB();
            ProcessDB(true); 
        }
        /// Always load for last 24 hours
        public void ProcessDB(bool isInit=false)
        {
            if (IsDBChanged())
            {
                lock (this.objLock)
                {
                    if(!isInit) MountRealTime2HistorianDB();
                    MountWriterDB();
                    if (!isInit) UnMountHistorianDB();
                }
            }
        }
        /// Write from ItemQueue to appropriate file
        public void Write2DB(uint TagID, Queue<DATA_POINT> TagQueue)
        {
            if (!DataTypeMap.ContainsKey(TagID)) return;

            int listCount = TagQueue.Count;
            if (listCount <= 0)
            {
                return;
            }

            // Copy To RealTime DB
            RealTimeDB[TagID]= TagQueue.LastOrDefault<DATA_POINT>();

            // Save to Historian DB
            UInt32 DataType = Convert.ToUInt32(DataTypeMap[TagID]);
            if (DataType == (UInt16)VarEnum.VT_BOOL) //tagVariant BOOL Type
            {
                var bf = (IWritableFeed<ulong, ItemULngByte>)WriterDB[TagID];
                lock (this.objLock)
                {
                    DataEngineStream<ItemULngByte>.Write(bf, ProcessByteForStore(TagQueue));
                }
            }
            else
            {
                var bf = (IWritableFeed<ulong, ItemULngULng>)WriterDB[TagID];
                lock (this.objLock)
                {
                    DataEngineStream<ItemULngULng>.Write(bf, ProcessULngForStore(TagQueue));
                }
            }
        }
        public void SetDataType(uint TagID, UInt16 DataType)
        {
            DataTypeMap.Add(TagID, DataType);
        }
        
        /// this function includes all for POINT,LIST,TREND
        public List<Object> GetData(List<Object> TagIDList, REQUEST_HEADER myRequest)
        {
                #region PROCESS DATA
                List<Object> myRet = new List<Object>();
                RequestType myRequestType = GetRequestType(myRequest);

                switch (myRequestType)
                {
                    case RequestType.PointData:
                        {
                            //Process for each Tag
                            for (int i = 0; i < TagIDList.Count; i++)
                            {
                                uint currentTagID = (uint)TagIDList[i];
                                if (myRequest.endtime == 0)
                                {
                                    if (RealTimeDB.ContainsKey(currentTagID))
                                        myRet.Add((DATA_POINT)RealTimeDB[currentTagID]);
                                }
                                else
                                {
                                    UInt16 DataType = GetDataType(currentTagID);

                                    if (DataType == (UInt16)VarEnum.VT_BOOL)
                                    {
                                        var bf = (IWritableFeed<ulong, ItemULngByte>)GetFileHandler(currentTagID, myRequest.endtime).FirstOrDefault();
                                        if (bf == null)
                                        {
                                            Console.WriteLine("==>POINT: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                        }
                                        else
                                        {
                                            lock (this.objLock)
                                            {
                                                myRet.Add(ProcessByteForSend(currentTagID, DataEngineStream<ItemULngByte>.ReadPoint(bf, myRequest.endtime)));
                                            }
                                        }
                                    }
                                    else
                                    {
                                        var bf = (IWritableFeed<ulong, ItemULngULng>)GetFileHandler(currentTagID, myRequest.endtime).FirstOrDefault();
                                        if (bf == null)
                                        {
                                            Console.WriteLine("==>POINT: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                        }
                                        else
                                        {
                                            lock (this.objLock)
                                            {
                                                myRet.Add(ProcessULngForSend(currentTagID, DataEngineStream<ItemULngULng>.ReadPoint(bf, myRequest.endtime)));
                                            }
                                        }
                                    }

                                }
                            }
                        }
                        break;
                    case RequestType.ListData:
                        {
                            //Process for each Tag
                            for (int i = 0; i < TagIDList.Count; i++)
                            {
                                uint currentTagID = (uint)TagIDList[i];
                                UInt16 DataType = GetDataType(currentTagID);
                                if (DataType == (UInt16)VarEnum.VT_BOOL)
                                {
                                    // if StartTime = 0 then process with the current Server Time
                                    myRequest.endtime = (myRequest.endtime == 0) ? (ulong)myTime.GetCurrentTimeAsJavaTime() : myRequest.endtime;

                                    var bfList = GetFileHandler(currentTagID, myRequest.endtime, myRequest.duration)
                                        .ConvertAll<IWritableFeed<ulong, ItemULngByte>>(delegate(Object obj) { return (IWritableFeed<ulong, ItemULngByte>)obj; });
                                    if (bfList.Count == 0)
                                    {
                                        Console.WriteLine("==>LIST: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                    }
                                    else
                                    {
                                        foreach (var bf in bfList)
                                        {
                                            lock (this.objLock)
                                            {
                                                List<ItemULngByte> myDataList= DataEngineStream<ItemULngByte>.ReadList(bf, myRequest.endtime, myRequest.duration);
                                                if (myDataList.Count > 0 && myDataList.First().SequenceNum < myRequest.endtime - myRequest.duration)
                                                {
                                                    myDataList.RemoveAt(0);
                                                }
                                                myRet.AddRange(ProcessByteForSend(currentTagID, myDataList));
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    // if StartTime = 0 then process with the current Server Time
                                    myRequest.endtime = (myRequest.endtime == 0) ? (ulong)myTime.GetCurrentTimeAsJavaTime() : myRequest.endtime;

                                    var bfList = GetFileHandler(currentTagID, myRequest.endtime, myRequest.duration)
                                           .ConvertAll<IWritableFeed<ulong, ItemULngULng>>(delegate(Object obj) { return (IWritableFeed<ulong, ItemULngULng>)obj; });
                                    if (bfList.Count == 0)
                                    {
                                        Console.WriteLine("==>LIST: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                    }
                                    else
                                    {
                                        foreach (var bf in bfList)
                                        {
                                            lock (this.objLock)
                                            {
                                                List<ItemULngULng> myDataList = DataEngineStream<ItemULngULng>.ReadList(bf, myRequest.endtime, myRequest.duration);
                                                if (myDataList.Count > 0 && myDataList.First().SequenceNum < myRequest.endtime - myRequest.duration)
                                                {
                                                    myDataList.RemoveAt(0);
                                                }
                                                myRet.AddRange(ProcessULngForSend(currentTagID, DataType,myDataList));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    case RequestType.TrendData:
                        {
                            //Process for each Tag
                            for (int i = 0; i < TagIDList.Count; i++)
                            {
                                uint currentTagID = (uint)TagIDList[i];
                                UInt16 DataType = GetDataType(currentTagID);
                                if (DataType == (UInt16)VarEnum.VT_BOOL)
                                {

                                    // if StartTime = 0 then process with the current Server Time
                                    myRequest.endtime = (myRequest.endtime == 0) ? (ulong)myTime.GetCurrentTimeAsJavaTime() : myRequest.endtime;

                                    var bfList = GetFileHandler(currentTagID, myRequest.endtime, myRequest.duration)
                                          .ConvertAll<IWritableFeed<ulong, ItemULngByte>>(delegate(Object obj) { return (IWritableFeed<ulong, ItemULngByte>)obj; });
                                    if (bfList.Count == 0)
                                    {
                                        Console.WriteLine("==>TREND: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                    }
                                    else
                                    {
                                        int Offset = 1;
                                        int BeforeCount = 0;
                                        bool isLastFile = false;
                                        foreach (var bf in bfList)
                                        {
                                            isLastFile = Object.Equals(bf, bfList.Last());
                                            lock (this.objLock)
                                            {
                                                BeforeCount = myRet.Count;
                                                myRet.AddRange(ProcessByteForSend(currentTagID, 
                                                    DataEngineStream<ItemULngByte>.ReadTrend(bf, myRequest.endtime, myRequest.duration, myRequest.resolution,Offset,isLastFile)));
                                                Offset += (myRet.Count - BeforeCount);
                                            }
                                        }
                                    }
                                }
                                else
                                {

                                    // if StartTime = 0 then process with the current Server Time
                                    myRequest.endtime = (myRequest.endtime == 0) ? (ulong)myTime.GetCurrentTimeAsJavaTime() : myRequest.endtime;

                                    var bfList = GetFileHandler(currentTagID, myRequest.endtime, myRequest.duration)
                                          .ConvertAll<IWritableFeed<ulong, ItemULngULng>>(delegate(Object obj) { return (IWritableFeed<ulong, ItemULngULng>)obj; });
                                    if (bfList.Count == 0)
                                    {
                                        Console.WriteLine("==>TREND: TagID[" + TagIDList[i].ToString() + "] Not found data");
                                    }
                                    else
                                    {
                                        int Offset = 1;
                                        int BeforeCount = 0;
                                        bool isLastFile = false;
                                        foreach (var bf in bfList)
                                        {
                                            isLastFile = Object.Equals(bf, bfList.Last());
                                            lock (this.objLock)
                                            {
                                                BeforeCount = myRet.Count;
                                                myRet.AddRange(ProcessULngForSend(currentTagID, DataType, 
                                                    DataEngineStream<ItemULngULng>.ReadTrend(bf, myRequest.endtime, myRequest.duration, myRequest.resolution, Offset, isLastFile)));
                                                Offset += (myRet.Count - BeforeCount);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }

                return myRet;
                #endregion
        }
        public RequestType GetRequestType(REQUEST_HEADER p_RequestHeader)
        {
            RequestType ret = RequestType.PointData;
            if (p_RequestHeader.duration != 0 && p_RequestHeader.resolution != 0)
            {
                ret = RequestType.TrendData;
            }
            else if (p_RequestHeader.duration != 0)
            {
                ret = RequestType.ListData;
            }
            else
            {
                ret = RequestType.PointData;
            }

            return ret;
        }
        #endregion

    }

}
